src: add CommitSizesEntry
authorFelix Krull <f_krull@gmx.de>
Tue, 25 Aug 2020 22:45:57 +0000 (00:45 +0200)
committerColin Walters <walters@verbum.org>
Fri, 6 May 2022 16:53:56 +0000 (12:53 -0400)
rust-bindings/rust/conf/ostree.toml
rust-bindings/rust/src/auto/commit_sizes_entry.rs [new file with mode: 0644]
rust-bindings/rust/src/auto/functions.rs
rust-bindings/rust/src/auto/mod.rs
rust-bindings/rust/src/auto/versions.txt
rust-bindings/rust/src/checksum.rs
rust-bindings/rust/src/commit_sizes_entry.rs [new file with mode: 0644]
rust-bindings/rust/src/lib.rs
rust-bindings/rust/src/repo_checkout_at_options.rs [deleted file]
rust-bindings/rust/src/repo_checkout_at_options/mod.rs [new file with mode: 0644]
rust-bindings/rust/sys/src/auto/versions.txt

index 8b905fcd18985ec68720ce0be44066a8f47ec267..ae0a5710b534400ab08424b7ae2fb95f3b936dc1 100644 (file)
@@ -14,6 +14,7 @@ generate = [
     "OSTree.AsyncProgress",
     "OSTree.BootconfigParser",
     "OSTree.ChecksumFlags",
+    "OSTree.CommitSizesEntry",
     "OSTree.Deployment",
     "OSTree.DeploymentUnlockedState",
     "OSTree.DiffFlags",
diff --git a/rust-bindings/rust/src/auto/commit_sizes_entry.rs b/rust-bindings/rust/src/auto/commit_sizes_entry.rs
new file mode 100644 (file)
index 0000000..0dc8691
--- /dev/null
@@ -0,0 +1,29 @@
+// This file was generated by gir (https://github.com/gtk-rs/gir)
+// from gir-files (https://github.com/gtk-rs/gir-files)
+// DO NOT EDIT
+
+#[cfg(any(feature = "v2020_1", feature = "dox"))]
+use glib::translate::*;
+use ostree_sys;
+#[cfg(any(feature = "v2020_1", feature = "dox"))]
+use ObjectType;
+
+glib_wrapper! {
+    #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+    pub struct CommitSizesEntry(Boxed<ostree_sys::OstreeCommitSizesEntry>);
+
+    match fn {
+        copy => |ptr| ostree_sys::ostree_commit_sizes_entry_copy(mut_override(ptr)),
+        free => |ptr| ostree_sys::ostree_commit_sizes_entry_free(ptr),
+        get_type => || ostree_sys::ostree_commit_sizes_entry_get_type(),
+    }
+}
+
+impl CommitSizesEntry {
+    #[cfg(any(feature = "v2020_1", feature = "dox"))]
+    pub fn new(checksum: &str, objtype: ObjectType, unpacked: u64, archived: u64) -> Option<CommitSizesEntry> {
+        unsafe {
+            from_glib_full(ostree_sys::ostree_commit_sizes_entry_new(checksum.to_glib_none().0, objtype.to_glib(), unpacked, archived))
+        }
+    }
+}
index 82e398b69bfd0e7aec3da5833ec458571a647167..8749b57beeefad139f97b76a11507a6918fddd2c 100644 (file)
@@ -10,6 +10,8 @@ use glib::GString;
 use ostree_sys;
 use std::mem;
 use std::ptr;
+#[cfg(any(feature = "v2020_1", feature = "dox"))]
+use CommitSizesEntry;
 use DiffFlags;
 use DiffItem;
 use ObjectType;
@@ -105,10 +107,15 @@ pub fn commit_get_content_checksum(commit_variant: &glib::Variant) -> Option<GSt
     }
 }
 
-//#[cfg(any(feature = "v2020_1", feature = "dox"))]
-//pub fn commit_get_object_sizes(commit_variant: &glib::Variant, out_sizes_entries: /*Ignored*/Vec<CommitSizesEntry>) -> Result<(), glib::Error> {
-//    unsafe { TODO: call ostree_sys:ostree_commit_get_object_sizes() }
-//}
+#[cfg(any(feature = "v2020_1", feature = "dox"))]
+pub fn commit_get_object_sizes(commit_variant: &glib::Variant) -> Result<Vec<CommitSizesEntry>, glib::Error> {
+    unsafe {
+        let mut out_sizes_entries = ptr::null_mut();
+        let mut error = ptr::null_mut();
+        let _ = ostree_sys::ostree_commit_get_object_sizes(commit_variant.to_glib_none().0, &mut out_sizes_entries, &mut error);
+        if error.is_null() { Ok(FromGlibPtrContainer::from_glib_container(out_sizes_entries)) } else { Err(from_glib_full(error)) }
+    }
+}
 
 pub fn commit_get_parent(commit_variant: &glib::Variant) -> Option<GString> {
     unsafe {
index 468b873bdb4933c85c4878f37e514246ee2abce8..e6494dcaea6e125366cf051348bcf33b92876d36 100644 (file)
@@ -80,6 +80,11 @@ mod collection_ref;
 #[cfg(any(feature = "v2018_6", feature = "dox"))]
 pub use self::collection_ref::CollectionRef;
 
+#[cfg(any(feature = "v2020_1", feature = "dox"))]
+mod commit_sizes_entry;
+#[cfg(any(feature = "v2020_1", feature = "dox"))]
+pub use self::commit_sizes_entry::CommitSizesEntry;
+
 mod diff_item;
 pub use self::diff_item::DiffItem;
 
index 85a1b25d4e57609913c44397c8723b4b85cef42e..557e5d3e0ae7acc07b061fe52ad9ace405a127bb 100644 (file)
@@ -1,2 +1,2 @@
 Generated by gir (https://github.com/gtk-rs/gir @ 2d1ffab1)
-from gir-files (https://github.com/gtk-rs/gir-files @ eec42a9)
+from gir-files (https://github.com/gtk-rs/gir-files @ ff904f0)
index 097438b9d9efa33ed1674916a25e135710b34746..1058f718204ba7a97d4407734a588a03d9f0fb54 100644 (file)
@@ -1,9 +1,10 @@
-use glib::translate::{from_glib_full, FromGlibPtrFull};
-use glib::GString;
+use glib::{
+    translate::{from_glib_full, FromGlibPtrFull, FromGlibPtrNone},
+    GString,
+};
 use glib_sys::{g_free, g_malloc, g_malloc0, gpointer};
 use libc::c_char;
-use std::fmt;
-use std::ptr::copy_nonoverlapping;
+use std::{fmt, ptr::copy_nonoverlapping};
 
 const BYTES_LEN: usize = ostree_sys::OSTREE_SHA256_DIGEST_LEN as usize;
 const HEX_LEN: usize = ostree_sys::OSTREE_SHA256_STRING_LEN as usize;
@@ -16,6 +17,8 @@ pub struct Checksum {
 }
 
 impl Checksum {
+    pub const DIGEST_LEN: usize = BYTES_LEN;
+
     /// Create a `Checksum` value, taking ownership of the given memory location.
     ///
     /// # Safety
@@ -23,11 +26,20 @@ impl Checksum {
     /// `g_free` (this is e.g. the case if the memory was allocated with `g_malloc`). The value
     /// takes ownership of the memory, i.e. the memory is freed when the value is dropped. The
     /// memory must not be freed by other code.
-    unsafe fn new(bytes: *mut [u8; BYTES_LEN]) -> Checksum {
+    unsafe fn new(bytes: *mut [u8; Self::DIGEST_LEN]) -> Checksum {
         assert!(!bytes.is_null());
         Checksum { bytes }
     }
 
+    /// Create a `Checksum` from a byte array.
+    pub fn from_bytes(checksum: &[u8; Self::DIGEST_LEN]) -> Checksum {
+        let ptr = checksum as *const [u8; BYTES_LEN] as *mut [u8; BYTES_LEN];
+        unsafe {
+            // Safety: we know this byte array is long enough.
+            Checksum::from_glib_none(ptr)
+        }
+    }
+
     /// Create a `Checksum` from a hexadecimal SHA256 string.
     ///
     /// Unfortunately, the underlying libostree function has no way to report parsing errors. If the
@@ -93,12 +105,7 @@ impl Drop for Checksum {
 
 impl Clone for Checksum {
     fn clone(&self) -> Self {
-        unsafe {
-            let cloned = g_malloc(BYTES_LEN) as *mut [u8; BYTES_LEN];
-            // copy one array of 32 elements
-            copy_nonoverlapping::<[u8; BYTES_LEN]>(self.bytes, cloned, 1);
-            Checksum::new(cloned)
-        }
+        unsafe { Checksum::from_glib_none(self.bytes) }
     }
 }
 
@@ -140,6 +147,15 @@ impl FromGlibPtrFull<*mut u8> for Checksum {
     }
 }
 
+impl FromGlibPtrNone<*mut [u8; BYTES_LEN]> for Checksum {
+    unsafe fn from_glib_none(ptr: *mut [u8; BYTES_LEN]) -> Self {
+        let cloned = g_malloc(BYTES_LEN) as *mut [u8; BYTES_LEN];
+        // copy one array of 32 elements
+        copy_nonoverlapping::<[u8; BYTES_LEN]>(ptr, cloned, 1);
+        Checksum::new(cloned)
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -156,6 +172,13 @@ mod tests {
         assert_eq!(checksum.to_string(), "00".repeat(BYTES_LEN));
     }
 
+    #[test]
+    fn should_create_checksum_from_bytes_copy() {
+        let bytes = [0u8; BYTES_LEN];
+        let checksum = Checksum::from_bytes(&bytes);
+        assert_eq!(checksum.to_string(), "00".repeat(BYTES_LEN));
+    }
+
     #[test]
     fn should_parse_checksum_string_to_bytes() {
         let csum = Checksum::from_hex(CHECKSUM_STRING);
diff --git a/rust-bindings/rust/src/commit_sizes_entry.rs b/rust-bindings/rust/src/commit_sizes_entry.rs
new file mode 100644 (file)
index 0000000..d96b4f0
--- /dev/null
@@ -0,0 +1,48 @@
+use crate::{auto::CommitSizesEntry, auto::ObjectType};
+use glib::{
+    translate::{FromGlib, FromGlibPtrNone, ToGlibPtr},
+    GString,
+};
+
+impl CommitSizesEntry {
+    /// Object checksum as hex string.
+    pub fn checksum(&self) -> GString {
+        let underlying = self.to_glib_none();
+        unsafe { GString::from_glib_none((*underlying.0).checksum) }
+    }
+
+    /// The object type.
+    pub fn objtype(&self) -> ObjectType {
+        let underlying = self.to_glib_none();
+        unsafe { ObjectType::from_glib((*underlying.0).objtype) }
+    }
+
+    /// Unpacked object size.
+    pub fn unpacked(&self) -> u64 {
+        let underlying = self.to_glib_none();
+        unsafe { (*underlying.0).unpacked }
+    }
+
+    /// Compressed object size.
+    pub fn archived(&self) -> u64 {
+        let underlying = self.to_glib_none();
+        unsafe { (*underlying.0).archived }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    const CHECKSUM_STRING: &str =
+        "bf875306783efdc5bcab37ea10b6ca4e9b6aea8b94580d0ca94af120565c0e8a";
+
+    #[test]
+    fn should_get_values_from_commit_sizes_entry() {
+        let entry = CommitSizesEntry::new(CHECKSUM_STRING, ObjectType::Commit, 15, 16).unwrap();
+        assert_eq!(entry.checksum(), CHECKSUM_STRING);
+        assert_eq!(entry.objtype(), ObjectType::Commit);
+        assert_eq!(entry.unpacked(), 15);
+        assert_eq!(entry.archived(), 16);
+    }
+}
index 6046c7c3d0b48a86dcd7d782658698f149789f0e..64f435cc9444972bcb27d1776ab8436d34a7a3f0 100644 (file)
@@ -29,33 +29,30 @@ pub use crate::auto::*;
 // handwritten code
 mod checksum;
 pub use crate::checksum::*;
-
 #[cfg(any(feature = "v2018_6", feature = "dox"))]
 mod collection_ref;
 #[cfg(any(feature = "v2018_6", feature = "dox"))]
 pub use crate::collection_ref::*;
-
 mod functions;
 pub use crate::functions::*;
-
 #[cfg(any(feature = "v2019_3", feature = "dox"))]
 mod kernel_args;
 #[cfg(any(feature = "v2019_3", feature = "dox"))]
 pub use crate::kernel_args::*;
-
 mod object_name;
 pub use crate::object_name::*;
-
 mod repo;
 pub use crate::repo::*;
-
 #[cfg(any(feature = "v2016_8", feature = "dox"))]
 mod repo_checkout_at_options;
 #[cfg(any(feature = "v2016_8", feature = "dox"))]
 pub use crate::repo_checkout_at_options::*;
-
 mod se_policy;
 pub use crate::se_policy::*;
+#[cfg(any(feature = "v2020_1", feature = "dox"))]
+mod commit_sizes_entry;
+#[cfg(any(feature = "v2020_1", feature = "dox"))]
+pub use crate::commit_sizes_entry::*;
 
 // tests
 #[cfg(test)]
diff --git a/rust-bindings/rust/src/repo_checkout_at_options.rs b/rust-bindings/rust/src/repo_checkout_at_options.rs
deleted file mode 100644 (file)
index 7d92e54..0000000
+++ /dev/null
@@ -1,263 +0,0 @@
-use crate::{RepoCheckoutMode, RepoCheckoutOverwriteMode, RepoDevInoCache, SePolicy};
-use glib::translate::*;
-use libc::c_char;
-use ostree_sys::*;
-use std::path::PathBuf;
-
-#[cfg(any(feature = "v2018_2", feature = "dox"))]
-mod repo_checkout_filter;
-#[cfg(any(feature = "v2018_2", feature = "dox"))]
-pub use self::repo_checkout_filter::RepoCheckoutFilter;
-
-pub struct RepoCheckoutAtOptions {
-    pub mode: RepoCheckoutMode,
-    pub overwrite_mode: RepoCheckoutOverwriteMode,
-    pub enable_uncompressed_cache: bool,
-    pub enable_fsync: bool,
-    pub process_whiteouts: bool,
-    pub no_copy_fallback: bool,
-    #[cfg(any(feature = "v2017_6", feature = "dox"))]
-    pub force_copy: bool,
-    #[cfg(any(feature = "v2017_7", feature = "dox"))]
-    pub bareuseronly_dirs: bool,
-    #[cfg(any(feature = "v2018_9", feature = "dox"))]
-    pub force_copy_zerosized: bool,
-    pub subpath: Option<PathBuf>,
-    pub devino_to_csum_cache: Option<RepoDevInoCache>,
-    /// A callback function to decide which files and directories will be checked out from the
-    /// repo. See the documentation on [RepoCheckoutFilter](struct.RepoCheckoutFilter.html) for more
-    /// information on the signature.
-    ///
-    /// # Panics
-    /// This callback may not panic. If it does, `abort()` will be called to avoid unwinding across
-    /// an FFI boundary and into the libostree C code (which is Undefined Behavior). If you prefer to
-    /// swallow the panic rather than aborting, you can use `std::panic::catch_unwind` inside your
-    /// callback to catch and silence any panics that occur.
-    #[cfg(any(feature = "v2018_2", feature = "dox"))]
-    pub filter: Option<RepoCheckoutFilter>,
-    #[cfg(any(feature = "v2017_6", feature = "dox"))]
-    pub sepolicy: Option<SePolicy>,
-    pub sepolicy_prefix: Option<String>,
-}
-
-impl Default for RepoCheckoutAtOptions {
-    fn default() -> Self {
-        RepoCheckoutAtOptions {
-            mode: RepoCheckoutMode::None,
-            overwrite_mode: RepoCheckoutOverwriteMode::None,
-            enable_uncompressed_cache: false,
-            enable_fsync: false,
-            process_whiteouts: false,
-            no_copy_fallback: false,
-            #[cfg(feature = "v2017_6")]
-            force_copy: false,
-            #[cfg(feature = "v2017_7")]
-            bareuseronly_dirs: false,
-            #[cfg(feature = "v2018_9")]
-            force_copy_zerosized: false,
-            subpath: None,
-            devino_to_csum_cache: None,
-            #[cfg(feature = "v2018_2")]
-            filter: None,
-            #[cfg(feature = "v2017_6")]
-            sepolicy: None,
-            sepolicy_prefix: None,
-        }
-    }
-}
-
-type StringStash<'a, T> = Stash<'a, *const c_char, Option<T>>;
-type WrapperStash<'a, GlibT, WrappedT> = Stash<'a, *mut GlibT, Option<WrappedT>>;
-
-impl<'a> ToGlibPtr<'a, *const OstreeRepoCheckoutAtOptions> for RepoCheckoutAtOptions {
-    #[allow(clippy::type_complexity)]
-    type Storage = (
-        Box<OstreeRepoCheckoutAtOptions>,
-        StringStash<'a, PathBuf>,
-        StringStash<'a, String>,
-        WrapperStash<'a, OstreeRepoDevInoCache, RepoDevInoCache>,
-        WrapperStash<'a, OstreeSePolicy, SePolicy>,
-    );
-
-    // We need to make sure that all memory pointed to by the returned pointer is kept alive by
-    // either the `self` reference or the returned Stash.
-    fn to_glib_none(&'a self) -> Stash<*const OstreeRepoCheckoutAtOptions, Self> {
-        // Creating this struct from zeroed memory is fine since it's `repr(C)` and only contains
-        // primitive types. In fact, the libostree docs say to zero the struct. This means we handle
-        // the unused bytes correctly.
-        // The struct needs to be boxed so the pointer we return remains valid even as the Stash is
-        // moved around.
-        let mut options = Box::new(unsafe { std::mem::zeroed::<OstreeRepoCheckoutAtOptions>() });
-        options.mode = self.mode.to_glib();
-        options.overwrite_mode = self.overwrite_mode.to_glib();
-        options.enable_uncompressed_cache = self.enable_uncompressed_cache.to_glib();
-        options.enable_fsync = self.enable_fsync.to_glib();
-        options.process_whiteouts = self.process_whiteouts.to_glib();
-        options.no_copy_fallback = self.no_copy_fallback.to_glib();
-
-        #[cfg(feature = "v2017_6")]
-        {
-            options.force_copy = self.force_copy.to_glib();
-        }
-
-        #[cfg(feature = "v2017_7")]
-        {
-            options.bareuseronly_dirs = self.bareuseronly_dirs.to_glib();
-        }
-
-        #[cfg(feature = "v2018_9")]
-        {
-            options.force_copy_zerosized = self.force_copy_zerosized.to_glib();
-        }
-
-        // We keep these complex values alive by returning them in our Stash. Technically, some of
-        // these are being kept alive by `self` already, but it's better to be consistent here.
-        let subpath = self.subpath.to_glib_none();
-        options.subpath = subpath.0;
-        let sepolicy_prefix = self.sepolicy_prefix.to_glib_none();
-        options.sepolicy_prefix = sepolicy_prefix.0;
-        let devino_to_csum_cache = self.devino_to_csum_cache.to_glib_none();
-        options.devino_to_csum_cache = devino_to_csum_cache.0;
-
-        #[cfg(feature = "v2017_6")]
-        let sepolicy = {
-            let sepolicy = self.sepolicy.to_glib_none();
-            options.sepolicy = sepolicy.0;
-            sepolicy
-        };
-        #[cfg(not(feature = "v2017_6"))]
-        let sepolicy = None.to_glib_none();
-
-        #[cfg(feature = "v2018_2")]
-        {
-            if let Some(filter) = &self.filter {
-                options.filter_user_data = filter.to_glib_none().0;
-                options.filter = Some(repo_checkout_filter::filter_trampoline_unwindsafe);
-            }
-        }
-
-        Stash(
-            options.as_ref(),
-            (
-                options,
-                subpath,
-                sepolicy_prefix,
-                devino_to_csum_cache,
-                sepolicy,
-            ),
-        )
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use glib_sys::{GFALSE, GTRUE};
-    use std::ffi::{CStr, CString};
-    use std::ptr;
-
-    #[test]
-    fn should_convert_default_options() {
-        let options = RepoCheckoutAtOptions::default();
-        let stash = options.to_glib_none();
-        let ptr = stash.0;
-        unsafe {
-            assert_eq!((*ptr).mode, OSTREE_REPO_CHECKOUT_MODE_NONE);
-            assert_eq!((*ptr).overwrite_mode, OSTREE_REPO_CHECKOUT_OVERWRITE_NONE);
-            assert_eq!((*ptr).enable_uncompressed_cache, GFALSE);
-            assert_eq!((*ptr).enable_fsync, GFALSE);
-            assert_eq!((*ptr).process_whiteouts, GFALSE);
-            assert_eq!((*ptr).no_copy_fallback, GFALSE);
-            #[cfg(feature = "v2017_6")]
-            assert_eq!((*ptr).force_copy, GFALSE);
-            #[cfg(feature = "v2017_7")]
-            assert_eq!((*ptr).bareuseronly_dirs, GFALSE);
-            #[cfg(feature = "v2018_9")]
-            assert_eq!((*ptr).force_copy_zerosized, GFALSE);
-            assert_eq!((*ptr).unused_bools, [GFALSE; 4]);
-            assert_eq!((*ptr).subpath, ptr::null());
-            assert_eq!((*ptr).devino_to_csum_cache, ptr::null_mut());
-            assert_eq!((*ptr).unused_ints, [0; 6]);
-            assert_eq!((*ptr).unused_ptrs, [ptr::null_mut(); 3]);
-            #[cfg(feature = "v2018_2")]
-            assert_eq!((*ptr).filter, None);
-            #[cfg(feature = "v2018_2")]
-            assert_eq!((*ptr).filter_user_data, ptr::null_mut());
-            #[cfg(feature = "v2017_6")]
-            assert_eq!((*ptr).sepolicy, ptr::null_mut());
-            assert_eq!((*ptr).sepolicy_prefix, ptr::null());
-        }
-    }
-
-    #[test]
-    fn should_convert_non_default_options() {
-        let options = RepoCheckoutAtOptions {
-            mode: RepoCheckoutMode::User,
-            overwrite_mode: RepoCheckoutOverwriteMode::UnionIdentical,
-            enable_uncompressed_cache: true,
-            enable_fsync: true,
-            process_whiteouts: true,
-            no_copy_fallback: true,
-            #[cfg(feature = "v2017_6")]
-            force_copy: true,
-            #[cfg(feature = "v2017_7")]
-            bareuseronly_dirs: true,
-            #[cfg(feature = "v2018_9")]
-            force_copy_zerosized: true,
-            subpath: Some("sub/path".into()),
-            devino_to_csum_cache: Some(RepoDevInoCache::new()),
-            #[cfg(feature = "v2018_2")]
-            filter: RepoCheckoutFilter::new(|_repo, _path, _stat| {
-                crate::RepoCheckoutFilterResult::Skip
-            }),
-            #[cfg(feature = "v2017_6")]
-            sepolicy: Some(
-                SePolicy::new(&gio::File::new_for_path("a/b"), gio::NONE_CANCELLABLE).unwrap(),
-            ),
-            sepolicy_prefix: Some("prefix".into()),
-        };
-        let stash = options.to_glib_none();
-        let ptr = stash.0;
-        unsafe {
-            assert_eq!((*ptr).mode, OSTREE_REPO_CHECKOUT_MODE_USER);
-            assert_eq!(
-                (*ptr).overwrite_mode,
-                OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_IDENTICAL
-            );
-            assert_eq!((*ptr).enable_uncompressed_cache, GTRUE);
-            assert_eq!((*ptr).enable_fsync, GTRUE);
-            assert_eq!((*ptr).process_whiteouts, GTRUE);
-            assert_eq!((*ptr).no_copy_fallback, GTRUE);
-            #[cfg(feature = "v2017_6")]
-            assert_eq!((*ptr).force_copy, GTRUE);
-            #[cfg(feature = "v2017_7")]
-            assert_eq!((*ptr).bareuseronly_dirs, GTRUE);
-            #[cfg(feature = "v2018_9")]
-            assert_eq!((*ptr).force_copy_zerosized, GTRUE);
-            assert_eq!((*ptr).unused_bools, [GFALSE; 4]);
-            assert_eq!(
-                CStr::from_ptr((*ptr).subpath),
-                CString::new("sub/path").unwrap().as_c_str()
-            );
-            assert_eq!(
-                (*ptr).devino_to_csum_cache,
-                options.devino_to_csum_cache.to_glib_none().0
-            );
-            assert_eq!((*ptr).unused_ints, [0; 6]);
-            assert_eq!((*ptr).unused_ptrs, [ptr::null_mut(); 3]);
-            #[cfg(feature = "v2018_2")]
-            assert!((*ptr).filter == Some(repo_checkout_filter::filter_trampoline_unwindsafe));
-            #[cfg(feature = "v2018_2")]
-            assert_eq!(
-                (*ptr).filter_user_data,
-                options.filter.as_ref().unwrap().to_glib_none().0,
-            );
-            #[cfg(feature = "v2017_6")]
-            assert_eq!((*ptr).sepolicy, options.sepolicy.to_glib_none().0);
-            assert_eq!(
-                CStr::from_ptr((*ptr).sepolicy_prefix),
-                CString::new("prefix").unwrap().as_c_str()
-            );
-        }
-    }
-}
diff --git a/rust-bindings/rust/src/repo_checkout_at_options/mod.rs b/rust-bindings/rust/src/repo_checkout_at_options/mod.rs
new file mode 100644 (file)
index 0000000..7d92e54
--- /dev/null
@@ -0,0 +1,263 @@
+use crate::{RepoCheckoutMode, RepoCheckoutOverwriteMode, RepoDevInoCache, SePolicy};
+use glib::translate::*;
+use libc::c_char;
+use ostree_sys::*;
+use std::path::PathBuf;
+
+#[cfg(any(feature = "v2018_2", feature = "dox"))]
+mod repo_checkout_filter;
+#[cfg(any(feature = "v2018_2", feature = "dox"))]
+pub use self::repo_checkout_filter::RepoCheckoutFilter;
+
+pub struct RepoCheckoutAtOptions {
+    pub mode: RepoCheckoutMode,
+    pub overwrite_mode: RepoCheckoutOverwriteMode,
+    pub enable_uncompressed_cache: bool,
+    pub enable_fsync: bool,
+    pub process_whiteouts: bool,
+    pub no_copy_fallback: bool,
+    #[cfg(any(feature = "v2017_6", feature = "dox"))]
+    pub force_copy: bool,
+    #[cfg(any(feature = "v2017_7", feature = "dox"))]
+    pub bareuseronly_dirs: bool,
+    #[cfg(any(feature = "v2018_9", feature = "dox"))]
+    pub force_copy_zerosized: bool,
+    pub subpath: Option<PathBuf>,
+    pub devino_to_csum_cache: Option<RepoDevInoCache>,
+    /// A callback function to decide which files and directories will be checked out from the
+    /// repo. See the documentation on [RepoCheckoutFilter](struct.RepoCheckoutFilter.html) for more
+    /// information on the signature.
+    ///
+    /// # Panics
+    /// This callback may not panic. If it does, `abort()` will be called to avoid unwinding across
+    /// an FFI boundary and into the libostree C code (which is Undefined Behavior). If you prefer to
+    /// swallow the panic rather than aborting, you can use `std::panic::catch_unwind` inside your
+    /// callback to catch and silence any panics that occur.
+    #[cfg(any(feature = "v2018_2", feature = "dox"))]
+    pub filter: Option<RepoCheckoutFilter>,
+    #[cfg(any(feature = "v2017_6", feature = "dox"))]
+    pub sepolicy: Option<SePolicy>,
+    pub sepolicy_prefix: Option<String>,
+}
+
+impl Default for RepoCheckoutAtOptions {
+    fn default() -> Self {
+        RepoCheckoutAtOptions {
+            mode: RepoCheckoutMode::None,
+            overwrite_mode: RepoCheckoutOverwriteMode::None,
+            enable_uncompressed_cache: false,
+            enable_fsync: false,
+            process_whiteouts: false,
+            no_copy_fallback: false,
+            #[cfg(feature = "v2017_6")]
+            force_copy: false,
+            #[cfg(feature = "v2017_7")]
+            bareuseronly_dirs: false,
+            #[cfg(feature = "v2018_9")]
+            force_copy_zerosized: false,
+            subpath: None,
+            devino_to_csum_cache: None,
+            #[cfg(feature = "v2018_2")]
+            filter: None,
+            #[cfg(feature = "v2017_6")]
+            sepolicy: None,
+            sepolicy_prefix: None,
+        }
+    }
+}
+
+type StringStash<'a, T> = Stash<'a, *const c_char, Option<T>>;
+type WrapperStash<'a, GlibT, WrappedT> = Stash<'a, *mut GlibT, Option<WrappedT>>;
+
+impl<'a> ToGlibPtr<'a, *const OstreeRepoCheckoutAtOptions> for RepoCheckoutAtOptions {
+    #[allow(clippy::type_complexity)]
+    type Storage = (
+        Box<OstreeRepoCheckoutAtOptions>,
+        StringStash<'a, PathBuf>,
+        StringStash<'a, String>,
+        WrapperStash<'a, OstreeRepoDevInoCache, RepoDevInoCache>,
+        WrapperStash<'a, OstreeSePolicy, SePolicy>,
+    );
+
+    // We need to make sure that all memory pointed to by the returned pointer is kept alive by
+    // either the `self` reference or the returned Stash.
+    fn to_glib_none(&'a self) -> Stash<*const OstreeRepoCheckoutAtOptions, Self> {
+        // Creating this struct from zeroed memory is fine since it's `repr(C)` and only contains
+        // primitive types. In fact, the libostree docs say to zero the struct. This means we handle
+        // the unused bytes correctly.
+        // The struct needs to be boxed so the pointer we return remains valid even as the Stash is
+        // moved around.
+        let mut options = Box::new(unsafe { std::mem::zeroed::<OstreeRepoCheckoutAtOptions>() });
+        options.mode = self.mode.to_glib();
+        options.overwrite_mode = self.overwrite_mode.to_glib();
+        options.enable_uncompressed_cache = self.enable_uncompressed_cache.to_glib();
+        options.enable_fsync = self.enable_fsync.to_glib();
+        options.process_whiteouts = self.process_whiteouts.to_glib();
+        options.no_copy_fallback = self.no_copy_fallback.to_glib();
+
+        #[cfg(feature = "v2017_6")]
+        {
+            options.force_copy = self.force_copy.to_glib();
+        }
+
+        #[cfg(feature = "v2017_7")]
+        {
+            options.bareuseronly_dirs = self.bareuseronly_dirs.to_glib();
+        }
+
+        #[cfg(feature = "v2018_9")]
+        {
+            options.force_copy_zerosized = self.force_copy_zerosized.to_glib();
+        }
+
+        // We keep these complex values alive by returning them in our Stash. Technically, some of
+        // these are being kept alive by `self` already, but it's better to be consistent here.
+        let subpath = self.subpath.to_glib_none();
+        options.subpath = subpath.0;
+        let sepolicy_prefix = self.sepolicy_prefix.to_glib_none();
+        options.sepolicy_prefix = sepolicy_prefix.0;
+        let devino_to_csum_cache = self.devino_to_csum_cache.to_glib_none();
+        options.devino_to_csum_cache = devino_to_csum_cache.0;
+
+        #[cfg(feature = "v2017_6")]
+        let sepolicy = {
+            let sepolicy = self.sepolicy.to_glib_none();
+            options.sepolicy = sepolicy.0;
+            sepolicy
+        };
+        #[cfg(not(feature = "v2017_6"))]
+        let sepolicy = None.to_glib_none();
+
+        #[cfg(feature = "v2018_2")]
+        {
+            if let Some(filter) = &self.filter {
+                options.filter_user_data = filter.to_glib_none().0;
+                options.filter = Some(repo_checkout_filter::filter_trampoline_unwindsafe);
+            }
+        }
+
+        Stash(
+            options.as_ref(),
+            (
+                options,
+                subpath,
+                sepolicy_prefix,
+                devino_to_csum_cache,
+                sepolicy,
+            ),
+        )
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use glib_sys::{GFALSE, GTRUE};
+    use std::ffi::{CStr, CString};
+    use std::ptr;
+
+    #[test]
+    fn should_convert_default_options() {
+        let options = RepoCheckoutAtOptions::default();
+        let stash = options.to_glib_none();
+        let ptr = stash.0;
+        unsafe {
+            assert_eq!((*ptr).mode, OSTREE_REPO_CHECKOUT_MODE_NONE);
+            assert_eq!((*ptr).overwrite_mode, OSTREE_REPO_CHECKOUT_OVERWRITE_NONE);
+            assert_eq!((*ptr).enable_uncompressed_cache, GFALSE);
+            assert_eq!((*ptr).enable_fsync, GFALSE);
+            assert_eq!((*ptr).process_whiteouts, GFALSE);
+            assert_eq!((*ptr).no_copy_fallback, GFALSE);
+            #[cfg(feature = "v2017_6")]
+            assert_eq!((*ptr).force_copy, GFALSE);
+            #[cfg(feature = "v2017_7")]
+            assert_eq!((*ptr).bareuseronly_dirs, GFALSE);
+            #[cfg(feature = "v2018_9")]
+            assert_eq!((*ptr).force_copy_zerosized, GFALSE);
+            assert_eq!((*ptr).unused_bools, [GFALSE; 4]);
+            assert_eq!((*ptr).subpath, ptr::null());
+            assert_eq!((*ptr).devino_to_csum_cache, ptr::null_mut());
+            assert_eq!((*ptr).unused_ints, [0; 6]);
+            assert_eq!((*ptr).unused_ptrs, [ptr::null_mut(); 3]);
+            #[cfg(feature = "v2018_2")]
+            assert_eq!((*ptr).filter, None);
+            #[cfg(feature = "v2018_2")]
+            assert_eq!((*ptr).filter_user_data, ptr::null_mut());
+            #[cfg(feature = "v2017_6")]
+            assert_eq!((*ptr).sepolicy, ptr::null_mut());
+            assert_eq!((*ptr).sepolicy_prefix, ptr::null());
+        }
+    }
+
+    #[test]
+    fn should_convert_non_default_options() {
+        let options = RepoCheckoutAtOptions {
+            mode: RepoCheckoutMode::User,
+            overwrite_mode: RepoCheckoutOverwriteMode::UnionIdentical,
+            enable_uncompressed_cache: true,
+            enable_fsync: true,
+            process_whiteouts: true,
+            no_copy_fallback: true,
+            #[cfg(feature = "v2017_6")]
+            force_copy: true,
+            #[cfg(feature = "v2017_7")]
+            bareuseronly_dirs: true,
+            #[cfg(feature = "v2018_9")]
+            force_copy_zerosized: true,
+            subpath: Some("sub/path".into()),
+            devino_to_csum_cache: Some(RepoDevInoCache::new()),
+            #[cfg(feature = "v2018_2")]
+            filter: RepoCheckoutFilter::new(|_repo, _path, _stat| {
+                crate::RepoCheckoutFilterResult::Skip
+            }),
+            #[cfg(feature = "v2017_6")]
+            sepolicy: Some(
+                SePolicy::new(&gio::File::new_for_path("a/b"), gio::NONE_CANCELLABLE).unwrap(),
+            ),
+            sepolicy_prefix: Some("prefix".into()),
+        };
+        let stash = options.to_glib_none();
+        let ptr = stash.0;
+        unsafe {
+            assert_eq!((*ptr).mode, OSTREE_REPO_CHECKOUT_MODE_USER);
+            assert_eq!(
+                (*ptr).overwrite_mode,
+                OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_IDENTICAL
+            );
+            assert_eq!((*ptr).enable_uncompressed_cache, GTRUE);
+            assert_eq!((*ptr).enable_fsync, GTRUE);
+            assert_eq!((*ptr).process_whiteouts, GTRUE);
+            assert_eq!((*ptr).no_copy_fallback, GTRUE);
+            #[cfg(feature = "v2017_6")]
+            assert_eq!((*ptr).force_copy, GTRUE);
+            #[cfg(feature = "v2017_7")]
+            assert_eq!((*ptr).bareuseronly_dirs, GTRUE);
+            #[cfg(feature = "v2018_9")]
+            assert_eq!((*ptr).force_copy_zerosized, GTRUE);
+            assert_eq!((*ptr).unused_bools, [GFALSE; 4]);
+            assert_eq!(
+                CStr::from_ptr((*ptr).subpath),
+                CString::new("sub/path").unwrap().as_c_str()
+            );
+            assert_eq!(
+                (*ptr).devino_to_csum_cache,
+                options.devino_to_csum_cache.to_glib_none().0
+            );
+            assert_eq!((*ptr).unused_ints, [0; 6]);
+            assert_eq!((*ptr).unused_ptrs, [ptr::null_mut(); 3]);
+            #[cfg(feature = "v2018_2")]
+            assert!((*ptr).filter == Some(repo_checkout_filter::filter_trampoline_unwindsafe));
+            #[cfg(feature = "v2018_2")]
+            assert_eq!(
+                (*ptr).filter_user_data,
+                options.filter.as_ref().unwrap().to_glib_none().0,
+            );
+            #[cfg(feature = "v2017_6")]
+            assert_eq!((*ptr).sepolicy, options.sepolicy.to_glib_none().0);
+            assert_eq!(
+                CStr::from_ptr((*ptr).sepolicy_prefix),
+                CString::new("prefix").unwrap().as_c_str()
+            );
+        }
+    }
+}
index 85a1b25d4e57609913c44397c8723b4b85cef42e..557e5d3e0ae7acc07b061fe52ad9ace405a127bb 100644 (file)
@@ -1,2 +1,2 @@
 Generated by gir (https://github.com/gtk-rs/gir @ 2d1ffab1)
-from gir-files (https://github.com/gtk-rs/gir-files @ eec42a9)
+from gir-files (https://github.com/gtk-rs/gir-files @ ff904f0)